<?php
/**
 * Arquivo do Controlador principal
 * @package controlador
 * @version 1.0
 */

/**
 * Classe controladora principal do framework
 * 
 * @package controlador
 */
class Controlador {
  /**
   * Instância única do Controlador.
   * Implementação do padrão Singleton
   *
   * @var Controlador $instancia
   */
  private static $instancia;

  /**
   * Qual módulo será carregado.
   *
   * @var Modulo $modulo
  */
  private $modulo;
  
  /**
   * Array onde ser�o registradas diversar informações.
   * Implementação do padrão de projeto Registry que
   * sugere um local comum para repositório de informações.
   *
   * @var array $registro
   */
  private $registro;

  /**
   * Construtor privado do controlador.
   * Só será executado uma única vez por script.
   *
   */
  private function __construct() {
    $this->registro = array();
  }

  /**
   * Método que recupera a instância da classe Controlador
   * @return Controlador
   */
  public static function getInstancia() {
    if (!isset(self::$instancia)) {
      self::$instancia = new Controlador();
    }
    return self::$instancia;
  }
  /**
   * Método que busca uma informação
   * registrada no controlador
   *
   * @param string $dado Nome do dado a ser buscado
   * @return mixed|boolean
   */
  public function get($dado) {
    if (isset($this->registro[$dado])) {
      return $this->registro[$dado];
    }
    return false;
  }
  /**
   * Método que registra informação no controlador
   *
   * @param string $nome
   * @param mixed $valor
   */
  public function set($nome, $valor) {
    $this->registro[$nome] = $valor;
  }
  
  /**
   * Método que informa se o dado foi ou
   * não registrado no controlador
   *
   * @param string $dado
   * @return boolean
   */
  public function existe($dado) {
  	return array_key_exists($dado, $this->registro);
  }
  
  /**
   * Método que dá sequência ao processamento do script.
   * Deverá chamar a classe do módulo correto
   * @throws ControleException
   */
  public function despachar() {
    // Recuperando o módulo
    $modulo = $this->recuperarModulo();
    // Recuperando o método-ação
    $metodo = $this->recuperarAcao($modulo);
    // Recuperando os parâmetros
    $parametros = $this->recuperarParametros($metodo);
    // Dando sequência ao script
    $acao = $this->registro['acao'];
    eval ("\$this->modulo->$acao($parametros);");
  }
  
  
  /**
   * Método auxiliar que recupera o módulo requisitado
   *
   * @return Modulo
   */
  private function recuperarModulo(){
    $modulo = ucfirst($this->registro['modulo']);
    if (class_exists($modulo)) {
      $this->modulo = new $modulo();
    } else {
      throw new ControleException(ControleException::MODULO_INEXISTENTE);
    }
    return $modulo;
  }
  /**
   * Método auxiliar que recupera uma instância da classe
   * de reflexão ReflectionMethod referente ao 
   * método-ação escolhido.
   * @throws ControleException
   * @param Modulo $modulo
   * @return ReflectionMethod
   */
  private function recuperarAcao($modulo) {
    try {
      // Verificando se existe o método-ação
      // na classe encontrada
      $rc = new ReflectionClass($modulo);
      // Se não existir, a linha abaixo levantará
      // uma ReflectionException      
      $metodo = $rc->getMethod($this->registro['acao']);
      // Verificando se o método é visível
      if ($metodo->isPublic()) {
        return $metodo;
        // Se não for visivel, lenvantar ControleException
      } else {
        throw new ControleException(ControleException::ACAO_PROTEGIDA);
      }
      // Se houver ReflectionException, 
      // levantar ControleException
	 } catch (ReflectionException $ex) {    	
      throw new  ControleException(ControleException::ACAO_INEXISTENTE);
	 }
  }
  
  /**
   * Método auxiliar que recupera a string dos parâmetros
   * para o self::despachar() poder dar sequência ao script.
   * @throws ControleException
   * @param ReflectionMethod $metodo
   * @return string
   */
  private function recuperarParametros($metodo) {
    // Se o total de parâmetros suprir o número requerido
    if (@count($this->registro['parametros']) >= $metodo->getNumberOfRequiredParameters()) {
      // Recuperando os parâmetros registrados
      $parametros = "'";
      if (isset($this->registro['parametros'])) {
        $parametros .= implode("', '",$this->registro['parametros']);
      }
      $parametros .= "'"; 
      $parametros = str_replace("''", "", $parametros);
        
      return $parametros;
    } else {
      throw new ControleException(
            ControleException::PARAMETROS_INSUFICIENTES);
    }
  }
  
  /**
   * Método utilitario chamado pelo contrutor
   * para recuperar os dados $_POST e $_FILES
   */  
  private function capturarDados() {
    // Capturando dados de requisições POST
    $this->dados = $_POST;
    // Capturando arquivos enviados
    $this->arquivos = $_FILES;
  }  
}
?>